package com.fd.dao.base;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.fd.dao.base.common.CdType;
import com.fd.dao.base.common.Condition;
import com.fd.dao.base.common.Operators;
import com.fd.dao.base.common.PageInfo;
import com.fd.util.MyUtils;

public abstract class BaseDao<POJO> implements IBaseDao<POJO> {

	private static final long serialVersionUID = -8431286198088262858L;

	@Override
	public void save(POJO pojo) {
		em.persist(pojo);
	}

	@Override
	public POJO persist(POJO pojo) {
		em.persist(pojo);
		return pojo;
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getListOrderby(LinkedHashMap<String, String> orderby,
			Set<Condition> conditions) {
		return getList(1, 50000, conditions, orderby);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getListBySgCd(Set<Condition> conditions,
			String... propertys) {
		return this.getList(null, conditions, propertys);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public Long getCount(Set<Condition> conditions) {
		StringBuilder sqlbuf = new StringBuilder("select count(");
		sqlbuf.append(alias).append(")").append("  from ")
				.append(this.clazz.getSimpleName()).append("   ").append(alias)
				.append(this.getWhereSql(conditions));
		Query qr = em.createQuery(sqlbuf.toString());
		addConditionValue(qr, conditions);
		return (Long) qr.getSingleResult();
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public Long getCount() {
		return getCount(null);
	}

	@Override
	public void update(POJO pojo) {
		em.merge(pojo);
	}

	@Override
	public POJO updateAndRetrun(POJO pojo) {
		return em.merge(pojo);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getList(LinkedHashMap<String, String> orderby,
			Set<Condition> conditions, String... propertys) {
		return this.getList(1, 1000, conditions, orderby, propertys);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public POJO getReference(Serializable sid) {
		return em.getReference(this.clazz, sid);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public PageInfo<POJO> getPageInfo(List<POJO> dataList, Long datacount,
			int curPage, int pageSize) {
		return new PageInfo<POJO>(curPage, pageSize, datacount, dataList);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public <T> PageInfo<T> getOptionPageInfo(List<T> dataList, Long datacount,
			int curPage, int pageSize) {
		return new PageInfo<T>(curPage, pageSize, datacount, dataList);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public POJO get(Set<Condition> conditions) {
		return get(conditions, null);
	}

	@Override
	public void deleteByCondition(Set<Condition> conditions) {
		StringBuffer buf = new StringBuffer("delete from  ");
		buf.append(this.clazz.getSimpleName()).append("  ").append(alias);
		buf.append(getWhereSql(conditions));
		Query qr = em.createQuery(buf.toString());
		addConditionValue(qr, conditions);
		qr.executeUpdate();
	}

	@Override
	public void updateByCondition(Set<Condition> conditions,
			Map<String, Object> newValues) {
		if (newValues != null && newValues.size() > 0) {
			StringBuilder buf = new StringBuilder("update ");
			buf.append(this.clazz.getSimpleName()).append("   ").append(alias)
					.append("  ").append("set  ");
			Iterator<Entry<String, Object>> ite = newValues.entrySet()
					.iterator();
			while (ite.hasNext()) {
				Entry<String, Object> en = ite.next();
				buf.append(alias).append(".").append(en.getKey()).append("=")
						.append(":").append(alias).append(en.getKey());
				if (ite.hasNext()) {
					buf.append(",");
				}
			}

			buf.append(getWhereSql(conditions));

			Query qr = em.createQuery(buf.toString());

			if (newValues != null && newValues.size() > 0) {
				Set<Entry<String, Object>> entrySet = newValues.entrySet();
				for (Entry<String, Object> en : entrySet) {
					qr.setParameter(alias + en.getKey(), en.getValue());
				}

			}

			addConditionValue(qr, conditions);
			qr.executeUpdate();
		}

	}

	@Override
	public int update(Set<Condition> conditions, Map<String, Object> newValues) {
		if (newValues != null && newValues.size() > 0) {
			StringBuilder buf = new StringBuilder("update ");
			buf.append(this.clazz.getSimpleName()).append("   ").append(alias)
					.append("  ").append("set  ");
			Iterator<Entry<String, Object>> ite = newValues.entrySet()
					.iterator();
			while (ite.hasNext()) {
				Entry<String, Object> en = ite.next();
				buf.append(alias).append(".").append(en.getKey()).append("=")
						.append(":").append(alias).append(en.getKey());
				if (ite.hasNext()) {
					buf.append(",");
				}
			}

			buf.append(getWhereSql(conditions));

			Query qr = em.createQuery(buf.toString());

			if (newValues != null && newValues.size() > 0) {
				Set<Entry<String, Object>> entrySet = newValues.entrySet();
				for (Entry<String, Object> en : entrySet) {
					qr.setParameter(alias + en.getKey(), en.getValue());
				}

			}

			addConditionValue(qr, conditions);
			return qr.executeUpdate();
		}
		return 0;
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public double getAvgPropertyValue(Set<Condition> condition, String property) {
		if (property != null && !property.trim().equals("")) {
			StringBuilder buf = new StringBuilder("select  avg(");
			buf.append(alias).append(".").append(property).append(")")
					.append("  from  ").append(this.clazz.getSimpleName())
					.append("  ").append(alias);
			buf.append(getWhereSql(condition));
			Query qr = em.createQuery(buf.toString());
			addConditionValue(qr, condition);
			Object o = qr.getSingleResult();
			if (o != null) {
				return Double.valueOf(o.toString());
			}
		}

		return 0;
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public double getSumPropertyValue(Set<Condition> conditions, String property) {
		if (property != null && !property.trim().equals("")) {
			StringBuilder buf = new StringBuilder("select sum(");
			buf.append(alias).append(".").append(property).append(")")
					.append(" from ").append(this.clazz.getSimpleName())
					.append("  ").append(alias);
			buf.append(getWhereSql(conditions));
			Query qr = em.createQuery(buf.toString());
			addConditionValue(qr, conditions);
			Object o = qr.getSingleResult();
			if (o != null) {
				return Double.valueOf(o.toString());
			}
		}

		return 0;
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public double getFunctionPropertyValue(Set<Condition> conditions,
			String property, String functionName) {
		if (property != null && !property.trim().equals("")
				&& MyUtils.isNotEmpty(functionName)) {
			StringBuilder buf = new StringBuilder("select  " + functionName
					+ "(");
			buf.append(alias).append(".").append(property).append(")")
					.append(" from ").append(this.clazz.getSimpleName())
					.append("  ").append(alias);
			buf.append(getWhereSql(conditions));
			Query qr = em.createQuery(buf.toString());
			addConditionValue(qr, conditions);
			Object o = qr.getSingleResult();
			if (o != null) {
				return Double.valueOf(o.toString());
			}
		}

		return 0;
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public POJO get(Set<Condition> conditions, String... propertys) {
		String jpql = getSelectJPQL(propertys) + getWhereSql(conditions);
		Query qr = em.createQuery(jpql);
		addConditionValue(qr, conditions);
		if (propertys != null && propertys.length > 0
				&& !propertys[0].trim().equals("")) {
			List<POJO> list = packageObject(qr, propertys);
			if (list.size() == 1) {
				return list.get(0);
			}
		} else {
			List<POJO> list = qr.getResultList();
			if (list.size() == 1) {
				return list.get(0);
			}
		}
		return null;
	}

	@Override
	public void delete(POJO pojo) {
		em.remove(pojo);
	}

	@Override
	public void deleteById(Serializable... sid) {
		if (sid != null) {
			for (Serializable s : sid) {
				em.remove(em.getReference(this.clazz, s));
			}
		}
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public POJO getById(Serializable sid) {
		return em.find(this.clazz, sid);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getList(int curPage, int pageSize) {
		return getList(curPage, pageSize, null, null, null);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getList() {
		return getList(1, 100000, null, null, null);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getList(int pageSize) {
		return getList(1, pageSize, null, null, null);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getList(int curPage, int pageSize,
			LinkedHashMap<String, String> orderby) {
		return getList(curPage, pageSize, null, orderby, null);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getListOrderBy(LinkedHashMap<String, String> orderby) {
		return getList(1, 100000, orderby);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getListByCondition(Set<Condition> conditions) {
		return getList(1, 100000, conditions, null, null);
	}

	@Override
	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	public List<POJO> getList(int curPage, int pageSize,
			Set<Condition> conditions, LinkedHashMap<String, String> orderby) {
		return getList(curPage, pageSize, conditions, orderby, null);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@SuppressWarnings("unchecked")
	@Override
	public List<POJO> getList(int curPage, int pageSize,
			Set<Condition> conditions, LinkedHashMap<String, String> orderby,
			String... propertys) {
		String jpql = getSelectJPQL(propertys) + getWhereSql(conditions)
				+ getOrderbyJpSql(orderby);
		Query qr = em.createQuery(jpql);
		addConditionValue(qr, conditions);
		qr.setFirstResult((curPage - 1) * pageSize);
		qr.setMaxResults(pageSize);
		if (propertys != null && propertys.length > 0
				&& !propertys[0].trim().equals("")) {
			return packageObject(qr, propertys);
		} else {
			return qr.getResultList();
		}
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getList(int curPage, int pageSize,
			Set<Condition> conditions) {
		return getList(curPage, pageSize, conditions, null, null);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public List<POJO> getList(LinkedHashMap<String, String> orderby,
			String... propertys) {
		return getList(1, 50000, null, orderby);
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public PageInfo<POJO> getPageInfo(int curPage, int pageSize,
			Set<Condition> conditions, LinkedHashMap<String, String> orderby,
			String... propertys) {
		return new PageInfo<POJO>(curPage, pageSize, getCount(conditions),
				getList(curPage, pageSize, conditions, orderby, propertys));

	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public PageInfo<POJO> getPageInfo(int curPage, int pageSize,
			Set<Condition> conditions, LinkedHashMap<String, String> orderby) {
		return new PageInfo<POJO>(curPage, pageSize, getCount(conditions),
				getList(curPage, pageSize, conditions, orderby, null));

	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public PageInfo<POJO> getPageInfo(int curPage, int pageSize,
			Set<Condition> conditions) {
		return new PageInfo<POJO>(curPage, pageSize, getCount(conditions),
				getList(curPage, pageSize, conditions, null, null));

	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public PageInfo<POJO> getPageInfo(int curPage, int pageSize) {
		return new PageInfo<POJO>(curPage, pageSize, getCount(null), getList(
				curPage, pageSize, null, null, null));

	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public PageInfo<POJO> getPageInfo(int curPage, int pageSize,
			LinkedHashMap<String, String> orderby, String... propertys) {
		return new PageInfo<POJO>(curPage, pageSize, getCount(null), getList(
				curPage, pageSize, null, orderby, propertys));

	}

	@Override
	public void updateByList(List<POJO> poList) {
		if (MyUtils.isNotEmpty(poList)) {
			for (POJO p : poList) {
				update(p);
			}
		}
	}

	@Override
	public void saveBatch(List<POJO> pojos) {
		if (MyUtils.isNotEmpty(pojos)) {
			for (POJO p : pojos) {
				em.persist(p);
			}
		}
	}

	@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
	@Override
	public PageInfo<POJO> getPageInfo(int curPage, int pageSize,
			LinkedHashMap<String, String> orderby) {
		return new PageInfo<POJO>(curPage, pageSize, getCount(null), getList(
				curPage, pageSize, null, orderby, null));

	}

	/***********
	 * -------------------------------华丽分割线------------------------------------
	 * ------------
	 *******/
	/***
	 * 查询指定属性的值返回的是object数组需要调用此方法把数据转换成POJO对象返回，此方法封装实现细节
	 */
	@SuppressWarnings("unchecked")
	private List<POJO> packageObject(Query qr, String... propertys) {
		List list = new ArrayList();
		if (propertys.length == 1) {
			List vlist = qr.getResultList();
			for (Object ov : vlist) {
				try {
					Object obj = this.clazz.newInstance();
					Field fd = this.clazz.getDeclaredField(propertys[0]);
					fd.setAccessible(true);
					fd.set(obj, ov);
					list.add(obj);
				} catch (Exception e) {
					e.printStackTrace();
				}

			}

		} else {
			List<Object[]> vlist = qr.getResultList();
			for (Object[] ov : vlist) {
				try {
					Object obj = this.clazz.newInstance();
					for (int i = 0; i < propertys.length; i++) {
						Field fd = this.clazz.getDeclaredField(propertys[i]);
						fd.setAccessible(true);
						fd.set(obj, ov[i]);
					}
					list.add(obj);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return list;
	}

	/**
	 * 得到JPQL语句的select部分语句不包括WHERE部分
	 */
	private String getSelectJPQL(String... propertys) {
		StringBuilder buf = new StringBuilder(" select  ");
		if (propertys != null && propertys.length > 0
				&& !propertys[0].trim().equals("")) {
			for (int i = 0; i < propertys.length; i++) {
				buf.append(alias).append(".").append(propertys[i]);
				if (i < propertys.length - 1) {
					buf.append(",");
				}
			}
		} else {
			buf.append(alias);
		}
		buf.append("  from  ").append(this.clazz.getSimpleName()).append("   ")
				.append(alias);
		return buf.append("  ").toString();
	}

	/**
	 * 得到排序JPQL语句
	 * 
	 * @param orderbys
	 * @return
	 */
	private static String getOrderbyJpSql(LinkedHashMap<String, String> orderbys) {
		if (orderbys == null || orderbys.size() < 1) {
			return "";
		}
		StringBuilder buf = new StringBuilder(" order by  ");
		Set<Entry<String, String>> entrySet = orderbys.entrySet();
		Iterator<Entry<String, String>> ite = entrySet.iterator();
		while (ite.hasNext()) {
			Entry<String, String> en = ite.next();
			buf.append(alias).append(".").append(en.getKey()).append("  ")
					.append(en.getValue());
			if (ite.hasNext()) {
				buf.append(",");
			}
		}
		return buf.toString();
	}

	/**
	 * 动态获取此DAO操作的实体类型
	 */
	@SuppressWarnings("unchecked")
	private Class<POJO> getThisClass(Class clazz) {
		Type type = clazz.getGenericSuperclass();
		if (type instanceof ParameterizedType) {
			Type[] ts = ((ParameterizedType) type).getActualTypeArguments();
			return (Class<POJO>) ts[0];
		}
		throw new RuntimeException("DAO 继承出现错误！DAO的父类需要使用泛型却没有使用泛型。。");
	}

	/**
	 * 拼接 JPQL Where 语句
	 * 
	 * @param conditions
	 * @return
	 */
	private String getWhereSql(Set<Condition> conditions) {

		if (conditions != null && conditions.size() > 0) {
			StringBuilder bufWhere = new StringBuilder(" where  ");
			Iterator<Condition> ite = conditions.iterator();

			while (ite.hasNext()) {
				// 得到条件数据对象
				Condition cd = ite.next();

				// 拼接属性
				bufWhere.append(alias).append(".").append(cd.getProperty());

				// 判断是否需要做为空特殊处理
				boolean isnull = cd.getValue() == null
						|| cd.getValue().toString().trim().equals("");
				if (!isnull && cd.getCdType().equals(CdType.OG)) {
					judgeCondition(bufWhere, cd, isnull);
					bufWhere.append(alias).append(".").append(cd.getValue());
				} else {
					judgeCondition(bufWhere, cd, isnull);

					if (cd.getOper().equals(Operators.IN)) {
						bufWhere.append(" in (:").append(cd.getProperty())
								.append(")");
					} else if (cd.getOper().equals(Operators.LIKE)) {
						bufWhere.append(" like ");
					} else if (cd.getOper().equals(Operators.BETWEEN)) {
						bufWhere.append("  between  ").append(":")
								.append(alias).append(cd.getProperty())
								.append("  and  ").append(":")
								.append(cd.getProperty());
					}

					if (!cd.getOper().equals(Operators.BETWEEN)
							&& !cd.getOper().equals(Operators.IN)) {
						if (!isnull) {
							bufWhere.append(":").append(cd.getProperty());
						}
					}
				}
				if (ite.hasNext()) {
					bufWhere.append(" and  ");
				}

			}
			return bufWhere.toString();
		} else {
			return "";
		}
	}

	private void judgeCondition(StringBuilder bufWhere, Condition cd,
			boolean isnull) {
		// 开始判断
		if (cd.getOper().equals(Operators.EQ)) {
			if (isnull) {
				bufWhere.append(" is  null  ");
			} else {
				bufWhere.append("=");
			}

		} else if (cd.getOper().equals(Operators.NOT_EQ)) {
			if (isnull) {
				bufWhere.append("  is not null  ");
			} else {
				bufWhere.append("<>");
			}
		} else if (cd.getOper().equals(Operators.LE)) {
			bufWhere.append("<=");
		} else if (cd.getOper().equals(Operators.LT)) {
			bufWhere.append("<");
		} else if (cd.getOper().equals(Operators.GE)) {
			bufWhere.append(">=");
		} else if (cd.getOper().equals(Operators.GT)) {
			bufWhere.append(">");
		}
	}

	/**
	 * 给查询条件赋值
	 * 
	 * @param qr
	 * @param conditions
	 */
	private void addConditionValue(Query qr, Set<Condition> conditions) {
		if (conditions != null && conditions.size() > 0) {
			for (Condition cd : conditions) {
				if (cd.getCdType().equals(CdType.VL)) {
					if (cd.getOper().equals(Operators.LIKE)) {
						qr.setParameter(cd.getProperty(), "%" + cd.getValue()
								+ "%");
					} else if (cd.getOper().equals(Operators.BETWEEN)) {
						qr.setParameter(alias + cd.getProperty(),
								cd.getFirstValue());
						qr.setParameter(cd.getProperty(), cd.getValue());
					} else {
						if (cd.getValue() != null && !cd.getValue().equals("")) {
							qr.setParameter(cd.getProperty(), cd.getValue());
						}
					}
				}
			}
		}
	}

	/**************** --------------------------华丽分割线---------------------------------- *********/
	/**
	 * 实体别名
	 */
	private static String alias = "o";
	/**
	 * 实体管理器
	 * **/
	@PersistenceContext
	protected EntityManager em;
	/**
	 * 当前操作的实体对象类型
	 */
	private Class<POJO> clazz = getThisClass(getClass());
}
